import datetime
from collections import Counter
import math
from config.config import TYPE_MATCH_LIST


# 质量或者体积匹配度
def getLoadabilityMatch(VQ, VV, VL, CQ, CV, CL):
    """
    :param VQ: 车辆质量
    :param VV: 车辆体积
    :param VL: 车辆长度
    :param CQ: 货物长度
    :param CV: 车辆体积
    :param CL: 货物体积
    :return: 可装载行匹配度
    """

    # 初始化质量、体积和长度匹配度
    QualityMatchDegree = 0
    VolumnMatchDegree = 0
    LengthMatchDegree = 0

    VolumnMatchDegree = CV / VV
    QualityMatchDegree = CQ / VQ
    LengthMatchDegree = CL / VL

    # 如果货物质量体积分别都大于车辆的质量和体积则匹配度为0
    if QualityMatchDegree and VolumnMatchDegree and LengthMatchDegree > 1:
        QualityMatchDegree = 0
        VolumnMatchDegree = 0
        LengthMatchDegree = 0

    return QualityMatchDegree, VolumnMatchDegree, LengthMatchDegree


# 时间匹配度


# 车辆属性概率分布
def vehicleAttrDis(vehicle_queue):
    """
    计算车辆属性概率分布
    :param vehicle_queue:
    :return:
    """
    # 车辆的数量
    length = len(vehicle_queue)
    # 类型列表
    type_list = []
    # 质量列表
    quality_list = []
    # 体积列表
    volumn_list = []
    # 车长列表
    length_list = []
    # 起始地列表
    start_list = []
    # 目的地列表
    destination_list = []
    # 时间列表
    time_list = []
    # 将车辆中的对应所有出现的属性放入对应的属性列表
    for item, value in vehicle_queue.items():
        type_list.append(value[0])
        quality_list.append(value[1])
        volumn_list.append(value[2])
        length_list.append(value[3])
        start_list.append(value[4])
        destination_list.append(value[5])
        time_list.append(value[8])
    # type_count 存放所有类型分别出现的次数
    type_count = Counter(type_list)
    # quality_count 存放不同质量出现的次数
    quality_count = Counter(quality_list)
    # 存放不同体积出现的次数
    volumn_count = Counter(volumn_list)
    # 存放不同长度出现的次数
    length_count = Counter(length_list)

    # start_count表示起始地相同的有几个
    def cal_start_count(abc):
        start_count = 0
        for start in start_list:
            if abc == start:
                start_count += 1
        return start_count

    # destination_count表示目的地相同的有几个
    def cal_destination_count(abc):
        destination_count = 0
        for destination in destination_list:
            if abc == destination:
                destination_count += 1
        return destination_count

    # time_count 表示起始、截至时间相同的个数
    def cal_time_count(abc):
        time_count = 0
        for time in time_list:
            if abc == time:
                time_count += 1
        return time_count

    # 存放每一辆车的各个具体属性的分布
    attr_distri_list = []
    # 计算每辆车的属性分布
    for item, value in vehicle_queue.items():
        # 车辆的对应的各个属性分布
        vehicle_attr = []
        # 每一辆车的类型占总车辆的概率分布
        type_distri = type_count[value[0]] / length
        # 每一辆车对应的质量概率分布
        quality_distri = quality_count[value[1]] / length
        # 体积分布
        volumn_distri = volumn_count[value[2]] / length
        length_distri = length_count[value[3]] / length
        # 起始地相同分布
        start_distri = cal_start_count(value[4]) / length
        # 目的地相同分布
        destination_distri = cal_destination_count(value[5]) / length
        # 时间相同分布
        time_distri = cal_time_count(value[8]) / length
        vehicle_attr.append(type_distri)
        vehicle_attr.append(quality_distri)
        vehicle_attr.append(quality_distri)
        vehicle_attr.append(volumn_distri)
        vehicle_attr.append(length_distri)
        vehicle_attr.append(start_distri)
        vehicle_attr.append(destination_distri)
        vehicle_attr.append(time_distri)
        # 每一辆车的各个属性对应概率分布
        # 每一个属性占所有车辆个数的比例
        attr_distri_list.append(vehicle_attr)
    # 所有属性
    total_attr = 0
    for i in attr_distri_list:
        total_attr += sum(i)

    # 存放每一辆车的整体属性分布
    vehicle_attr_list = []
    for i in attr_distri_list:
        vehicle_attr_list.append(sum(i))
    # 车辆的属性概率（先验概率）最终的结果每种属性占所有属性的概率值
    # round函数四舍五入的方法 round(数值，2)取小数点前两位
    attr_distribution_final_all = [round(i / total_attr, 2) for i in vehicle_attr_list]
    return attr_distribution_final_all


# 货物属性的先验概率
def cargoAttrDis(cargo_queue):
    """
    计算货物的先验概率
    :param cargo_queue:
    :return:
    """
    length = len(cargo_queue)
    type_list = []
    quality_list = []
    volumn_list = []
    length_list = []
    start_list = []
    destination_list = []
    time_list = []
    for item, value in cargo_queue.items():
        type_list.append(value[0])
        quality_list.append(value[1])
        volumn_list.append(value[2])
        length_list.append(value[3])
        start_list.append(value[4])
        destination_list.append(value[5])
        time_list.append(value[8])

    # type_count各种类型出现的次数
    type_count = Counter(type_list)
    # quality_count存放各种质量出现的次数
    quality_count = Counter(quality_list)
    # volumn_count存放各种体积出现的次数
    volumn_count = Counter(volumn_list)
    length_count = Counter(length_list)

    # start_count表示起始地相同的有几个
    def cal_start_count(abc):
        # 初始化
        start_count = 0
        for start in start_list:
            if abc == start:
                start_count += 1
        return start_count

    # destination_count表示目的地相同的有几个
    def cal_destination_count(abc):
        # 初始化
        destination_count = 0
        for destination in destination_list:
            if destination == abc:
                destination_count += 1
        return destination_count

    # time_count表示起始时间和截至时间相同的个数
    def cal_time_count(abc):
        # 初始化
        time_count = 0
        for time in time_list:
            if time == abc:
                time_count += 1

        return time_count

    # 存放每一个货物的各个属性的具体分布
    attr_distri_list = []
    # 计算每个货物的属性分布
    for item, value in cargo_queue.items():
        vehicle_attr = []
        type_distri = type_count[value[0]] / length  # 每一个类型占总货物辆的概率分布
        qualisty_distri = quality_count[value[1]] / length
        volumn_distri = volumn_count[value[2]] / length
        length_distri = length_count[value[3]] / length
        start_distri = cal_start_count(value[4]) / length
        destination_distri = cal_destination_count(value[5]) / length
        time_distri = cal_time_count(value[8]) / length
        vehicle_attr.append(type_distri)  # 每一个类型的概率分布
        vehicle_attr.append(qualisty_distri)
        vehicle_attr.append(volumn_distri)
        vehicle_attr.append(length_distri)
        vehicle_attr.append(start_distri)
        vehicle_attr.append(destination_distri)
        vehicle_attr.append(time_distri)
        attr_distri_list.append(vehicle_attr)  # 各个属性的概率分布

    total_attr = 0
    for i in attr_distri_list:
        total_attr += sum(i)

    # 存放每一个货物的整体属性的分布
    vehicle_attr_list = []
    for i in attr_distri_list:
        vehicle_attr_list.append(sum(i))

    # 货物属性分布概率（先验结果）
    attr_distribution_final_all = [round(i / total_attr, 2) for i in vehicle_attr_list]
    return attr_distribution_final_all


def VCM(a, b, real_data_dict, w1=0.16, w2=0.09, w3=0.47, w4=0.28, w5=0.2, w6=0.13):
    """

    :param a:
    :param b:
    :param real_data_dict:
    :param w1: 以下均为权重
    :param w2:
    :param w3:
    :param w4:
    :param w5:
    :param w6:
    :return:
    """

    def haversine(lat1, lon1, lat2, lon2):
        # 将度转换为弧度
        lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])

        # Haversine公式
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) + math.sin(dlon / 2) ** 2
        c = 2 * math.asin(math.sqrt(a))

        # 地球半径 (公里)
        r = 6371.0

        # 计算距离
        distance = c * r
        return distance

    vehicle = {
        'type': a[0],
        'quality': a[1],
        'volume': a[2],
        'startPoint': a[4],
        'endPoint': a[5],
        'time': a[8],
    }
    cargo = {
        'type': b[0],
        'quality': b[1],
        'volume': b[2],
        'startPoint': b[4],
        'endPoint': b[5],
        'time': b[8],
    }
    # 总路程
    total_distance = (abs(vehicle['startPoint'][0] - cargo['startPoint'][0]) + abs(
        vehicle['startPoint'][1] - cargo['startPoint'][1]) + abs(cargo['startPoint'][0] - cargo['endPoint'][0])
                      + abs(cargo['startPoint'][1] - cargo['endPoint'][1]))
    # 有效路程
    cargoToDestination = abs(cargo['startPoint'][0] - cargo['endPoint'][0]) + abs(
        cargo['startPoint'][1] - cargo['endPoint'][1])

    # 对于车主：

    # 类型匹配
    def getTypeMatch(vehicle_type, cargo_type):
        """
        已完成
        :param vehicle_type:
        :param cargo_type:
        :return:
        """
        # d-vehicle1.csv='厢式车
        # d-cargo1.csv='特殊货物'
        # 初始化类型匹配度
        typeMatchDegree = 0
        for item in TYPE_MATCH_LIST:
            if vehicle_type == item[0] and cargo_type == item[1]:
                typeMatchDegree = item[2]
        return float(typeMatchDegree)

    vehicle_type_match = getTypeMatch(vehicle['type'], cargo['type'])

    # 载重匹配度
    # TODO: 缺少权重
    def getQualityVolumeMatch(vQ, vV, cQ, cV):
        """
        已完成
        :param vQ: 车辆质量
        :param vV: 车辆体积
        :param cQ: 货物质量
        :param cV: 货物体积
        :return:
        """
        # vQ = 8000
        # vV = 40
        # cQ = 5000
        # cV = 30
        QualityVolumeMatchDegree = 0
        # 判断货物是重货还是轻货，重货使用体积，轻货则使用体积
        if (cV / 0.006) > cQ:
            QualityVolumeMatchDegree = cV / vV
        else:
            QualityVolumeMatchDegree = cQ / vQ
        # 如果出现cQ>vQ,cV>Vv, 匹配度直接为0
        if QualityVolumeMatchDegree > 1:
            QualityVolumeMatchDegree = 0
        return float(QualityVolumeMatchDegree)

    vehicle_load_match = getQualityVolumeMatch(vehicle['quality'], vehicle['volume'],
                                               cargo['quality'], cargo['volume'])

    # 空驶率匹配度
    # 完成
    def getFlowMatch():
        """
        路径重合度
        :param VO: d-vehicle1.csv origin:车辆的起始地
        :param VD: d-vehicle1.csv destination:车辆的目的地
        :param CO: d-cargo1.csv origin:货物的起始地
        :param CD: d-cargo1.csv destination:货物的目的地
        :return:
        """
        # 分别表示，货物起始点到终点距离，车辆货物起始地距离，车辆货物终点距离
        cargo_origin_destination = 0
        vehicle_cargo_origin = 0
        vehicle_cargo_destination = 0
        flowMatchDegree = 0
        # VO = ",".join('%s' % id for id in VO)
        # VD = ",".join('%s' % id for id in VD)
        # CO = ",".join('%s' % id for id in CO)
        # CD = ",".join('%s' % id for id in CD)
        # print(f'VO, VD, CO, CD:{VO, VD, CO, CD}')
        # cargo_origin_destination = int(dt.getDistance(VO, VD)) // 1000
        # vehicle_cargo_origin = int(dt.getDistance(VO, CO)) // 1000
        # vehicle_cargo_destination = int(dt.getDistance(VD, CD)) // 1000
        # real_index = str(int(int(a_index) * 1000 + int(b_index)))
        # # 给两个索引
        # # print(f'a_index,b_index:{a_index,b_index}')
        # cargo_origin_destination = int(real_data_dict[real_index][1])
        # vehicle_cargo_origin = int(real_data_dict[real_index][2])
        # vehicle_cargo_destination = int(real_data_dict[real_index][3])
        # # print(
        # #     f'cargo_origin_destination,vehicle_cargo_origin,vehicle_cargo_destination:{cargo_origin_destination, vehicle_cargo_origin, vehicle_cargo_destination}')
        # # flowMatchDegree = cargo_origin_destination / (
        # #         cargo_origin_destination + vehicle_cargo_origin + vehicle_cargo_destination)
        # flowMatchDegree = vehicle_cargo_origin + vehicle_cargo_destination / (
        #         cargo_origin_destination + vehicle_cargo_origin + vehicle_cargo_destination)
        flow_match_degree = (total_distance - cargoToDestination) / total_distance
        return float(1 - flow_match_degree), float(total_distance - cargoToDestination)

    vehicle_flow_match, tocargo = getFlowMatch()

    # 时间匹配度
    def timeMatch(vS, vD, cS, cD):
        """
        :param vS: vehicle-start:车辆进入时间
        :param vD: vehicle_deadline:车辆截止时间
        :param cS: cargo-start:货物进入时间
        :param cD: cargo-deadline:货物截止时间
        :return:
        """
        timeMatchDegree = 0
        # vS = 2019050709
        # vD = 2019051915
        # cS = 2019051212
        # cD = 2019052516
        vS_list = [int(str(vS)[0:4]), int(str(vS)[4:6]), int(str(vS)[6:8])]
        vD_list = [int(str(vD)[0:4]), int(str(vD)[4:6]), int(str(vD)[6:8])]
        cS_list = [int(str(cS)[0:4]), int(str(cS)[4:6]), int(str(cS)[6:8])]
        cD_list = [int(str(cD)[0:4]), int(str(cD)[4:6]), int(str(cD)[6:8])]
        # 最小截止时间
        min_deadline = min(vD, cD)
        # 最大开始时间
        max_start = max(vS, cS)
        min_deadline_list = [int(str(min_deadline)[0:4]), int(str(min_deadline)[4:6]), int(str(min_deadline)[6:8])]
        max_start_list = [int(str(max_start)[0:4]), int(str(max_start)[4:6]), int(str(max_start)[6:8])]
        # print(min_deadline, max_start, min_deadline_list, max_start_list)
        # d1-d2表示最小截止时间减去最大开始时间。 d3-d4表示货物开始和截止时间之间的天数
        d1 = datetime.date(min_deadline_list[0], min_deadline_list[1], min_deadline_list[2])
        d2 = datetime.date(max_start_list[0], max_start_list[1], max_start_list[2])
        d3 = datetime.date(cS_list[0], cS_list[1], cS_list[2])
        d4 = datetime.date(cD_list[0], cD_list[1], cD_list[2])
        interval_days = (d1 - d2).days
        cargo_interval_days = (d4 - d3).days
        # print(interval_days, cargo_interval_days)
        if interval_days > 0:
            timeMatchDegree = interval_days / cargo_interval_days
        else:
            timeMatchDegree = 0
        # print(timeMatchDegree)
        return float(timeMatchDegree)

    def getTimeMatch(a1, a2, b1, b2):
        """

        :param vs:
        :param ve:
        :param cs:
        :param ce:
        :return:
        """
        vs = float(min(a1, a2))
        ve = float(max(a1, a2))
        cs = float(min(b1, b2))
        ce = float(max(b1, b2))
        if ce == cs:
            ce = cs + 1
        if (min(ve, ce) - max(vs, cs)) / (ce - cs) > 0:
            degree = (min(ve, ce) - max(vs, cs)) / (ce - cs)
        else:
            degree = 0

        return degree

    vehicle_time_match = getTimeMatch(a[8][0], a[8][1], b[8][0], b[8][1])

    # # 运费匹配度
    # def calculate_vehicle_price_match():
    #     """
    #     计算车主对于运费的匹配度
    #     车主在乎成本 和 成交价格
    #     :return:
    #     """
    #
    #     # 货物里程
    #     cost = float(total_distance) / 10000
    #     # 市场价  货物的起始地到目的地的总里程
    #     price = cargoToDestination / 10000 + float(cargo['quality']) / 10000 + float(cargo['volume']) / 60
    #     # 成本价 自行驶的总里程 成本价/市场价（成本价 = 总路程，市场价 = 货物重量，体积，货物运输的路程）
    #     match = float((price - cost) / price)
    #     return match
    #
    # vehicle_price_match = calculate_vehicle_price_match()
    # 车辆属性匹配度
    w1, w2, w3, w4 = 0.3, 0.3, 0.8, 0.3
    vehicle_w = [w1, w2, w3, w4]
    vehicleAttrMatch = (w1 * vehicle_type_match + w2 * vehicle_load_match +
                        w3 * vehicle_flow_match + w4 * vehicle_time_match)
    # print(
    #     f"vehicle_type_match：{vehicle_type_match}\n"
    #     f", vehicle_load_match:{vehicle_load_match}\n"
    #     f",vehicle_flow_match:{vehicle_flow_match}\n,"
    #     f"vehicle_time_match:{vehicle_time_match}\n")
    # 对于属性匹配度，保留两位小数
    vehicleAttrMatch = round(vehicleAttrMatch, 2)
    # 如果任何一个属性匹配度为为0 则不满足匹配的条件
    if vehicle_type_match == 0 or vehicle_flow_match < 0.5 or vehicle_load_match == 0 or vehicle_time_match == 0:
        vehicleAttrMatch = 0

    # 货主

    # 时间匹配度
    # def calculate_cargo_time_match():
    #     """
    #
    #     :return:
    #     """
    #     degree = (cargo['time'][1] - vehicle['time'][1]) / (cargo['time'][1] - cargo['time'][0])
    #     if degree > 1:
    #         degree = 1
    #     if degree < 0:
    #         degree = 0
    #     return float(degree)
    #
    # cargo_time_match = calculate_cargo_time_match()

    # # 运费匹配度
    # def calculate_cargo_price_match():
    #     """
    #     货主方的价格满意度
    #     货主重量，体积，路程等方面
    #     :return:
    #     """
    #     # TODO 权重还需要优化
    #     p_w1, p_w2, p_w3 = 0.3, 0.3, 0.4
    #     degree = (p_w1 * cargo['volume'] / 60. + p_w2 * cargo['quality'] / 10000. +
    #               p_w3 * haversine(cargo['startPoint'][0],
    #                                cargo['startPoint'][1],
    #                                cargo['endPoint'][0],
    #                                cargo['endPoint'][1]) / 3000.)
    #     degree =  (degree) / (degree + total_distance)
    #     return float(degree)
    #
    # cargo_price_match = calculate_cargo_price_match()

    # 载重匹配度
    # def calculate_cargo_load_match():
    #     if cargo['quality'] > vehicle['quality'] or cargo['volume'] > vehicle['volume']:
    #         degree = 0
    #     else:
    #         degree = 1
    #
    #     return float(degree)
    #
    # cargo_load_match = calculate_cargo_load_match()

    # w1, w2 = 0.3, 0.7
    # cargo_w = [w1, w2, w3]
    # cargo_attr_match = w1 * cargo_load_match + w2 * cargo_time_match
    doubleS = vehicleAttrMatch
    sign = 1
    attr_list = [doubleS,
                 vehicleAttrMatch,
                 vehicle_type_match, vehicle_load_match, vehicle_flow_match, vehicle_time_match, tocargo,
                 sign]
    has_zero = any(attr == 0 for attr in attr_list)
    if has_zero:
        attr_list[len(attr_list) - 1] = 0
    return attr_list


# 从数据集中读取数据并添加到vehicle_queue
def appendVehicle(vehicle_df, vehicle_queue, max_vehicle_index):
    """
    TODO 2024/1/30 这个函数目前有问题，需要进行修改，
    问题为：上个时间片剩下的车辆并未被使用，而是直接进入了新的车辆
    :param vehicle_df:
    :param vehicle_queue:
    :param t:
    :param max_vehicle_index:
    :return:
    """
    # 如果车辆队列中的车辆少于10辆，那么读取数据集并添加至少10辆车
    # print(f'剩余的车辆:{vehicle_queue}')
    length = len(vehicle_queue)
    if (len(vehicle_queue) < 10):
        for index, row in vehicle_df.iterrows():  # .iterrows()返回行索引及包含行本身的对象

            # 从头开始遍历表
            vehicle_attr = []
            # 上一次vehicle_queue中存放着的车辆的最大编号是num,那么当车辆数少于10辆的时候，应该从num+1开始读数据

            if index > max_vehicle_index:
                # 依次加入十个车辆
                for i in range(0, len(row)):  # 遍历每一行的元素
                    if i == 1:
                        row[i] = int(row[i])  # 质量
                    if i == 2:
                        row[i] = int(row[i])  # 体积
                    if i == 3:
                        row[i] = int(row[i])  # 长度
                    if i == 4 or i == 5:  # 路径
                        row[i] = str(row[i])[1:-1]
                        row[i] = row[i].split(',')
                        for j in range(0, len(row[i])):
                            row[i][j] = float(row[i][j])
                    if i == 6:
                        row[i] = row[i]  # 编码后的路径

                    if i == 7:  # 路径
                        row[i] = row[i]
                    if i == 8:
                        row[i] = str(row[i])[1:-1]
                        row[i] = row[i].split(',')
                        for j in range(0, len(row[i])):
                            row[i][j] = int(row[i][j])
                    # 每一个车辆的属性
                    vehicle_attr.append(row[i])
                    # 将车辆的信息添加至车辆队列中

                vehicle_queue[length] = vehicle_attr
                length += 1
                # 保存上个时间片添加的车辆的最大的编号
                if len(vehicle_queue) == 10:
                    # 记录最大值
                    # vehicle_max_Num.append(index)
                    return vehicle_queue


# 从数据集中读取数据添加到cargo_queue
# 参数1：数据集的df形式，2：队列中最大的货物编号，3：货物队列，4：时间
def appendCargo(cargo_df, cargo_queue, max_vehicle_index):
    # 如果货物的数量少于十个，则读取数据集并添加至十个货物
    if (len(cargo_queue)) < 10:
        for index, row in cargo_df.iterrows():
            cargo_attr = []
            # 上一次vehicle_queue存储的货物的最大编号为num那么少于十个的时候就要添加至十个，从num+1开始读取
            length = len(cargo_queue)
            # 0为表头 将数据集中的数据添加到货物队列
            if index > max_vehicle_index:  # 将数据集中的数据类型做转换
                for i in range(0, len(row)):
                    if i == 1:  # 质量
                        row[i] = int(row[i])
                    if i == 2:  # 体积
                        row[i] = int(row[i])
                    if i == 3:  # 长度
                        row[i] = int(row[i])
                    if i == 4 or i == 5:  # 路径
                        row[i] = str(row[i])[1:-1]
                        row[i] = row[i].split(',')
                        for j in range(0, len(row[i])):
                            row[i][j] = float(row[i][j])
                    if i == 6:  # 编码后的路径
                        row[i] = row[i]
                    if i == 7:
                        row[i] = row[i]
                    if i == 8:
                        row[i] = str(row[i])[1:-1]
                        row[i] = row[i].split(',')
                        for j in range(0, len(row[i])):
                            row[i][j] = int(row[i][j])

                    cargo_attr.append(row[i])
                cargo_queue[length] = cargo_attr
                length += 1
                if len(cargo_queue) == 10:
                    # cargo_maxNum.append(index)
                    return cargo_queue
